import java.util.*;
import java.util.regex.*;

public class LexicalAnalyzer {

    private static final String KW_PATTERN = "\\b(int)|(float)|(char)|(void)|(return)|(const)|(main)|(struct)|(union)|(switch)|(case)\\b";
    private static final String IDN_PATTERN = "[a-zA-Z_][a-zA-Z0-9_]*";
    private static final String OP_PATTERN = "[!\\+\\-\\*\\/\\%\\=\\>\\<\\!\\&\\|]+";
    private static final String SE_PATTERN = "[(){};\\,]";
    private static final String CHAR_PATTERN = "'(.)'";
    //private final List<Token> tokens;
    private int state;
    

    public List<Token1> analyze(String input) {
        List<Token1> tokens = new ArrayList<>();
        int pos = 0;
        String valueF = "";
        while (pos < input.length()) {
            char currentChar = input.charAt(pos);
            String value = "";
            if (Character.isWhitespace(currentChar)) {
                pos++;
                continue;
            }
            switch (state) {
                case 0:
                if (matchesPattern(input, pos, KW_PATTERN)) {
                    changeto(1);
                }
                else if (matchesPattern(input, pos, SE_PATTERN)) {
                    changeto(2);
                }
                else if (matchesPattern(input, pos, OP_PATTERN)) {
                    changeto(3);
                }
                else if (isDigital(input.charAt(pos))) {
                    changeto(4);
                }
                else if (isLetter(input.charAt(pos))) {
                    changeto(7);
                }
                else if (matchesPattern(input, pos, CHAR_PATTERN)) {
                    changeto(9);
                }
                else if (input.charAt(pos) == '_') {
                    changeto(10);
                }else {
                    throw new IllegalArgumentException("Unknown symbol at position " + pos);
                }
                break;
                case 1:
                value = matchAndConsume(input, pos, KW_PATTERN);
                switch (value) {
                	case "int":tokens.add(new Token1(TokenType.KW, value, "1")); break;
                	case "float":tokens.add(new Token1(TokenType.KW, value, "2")); break;
                	case "char":tokens.add(new Token1(TokenType.KW, value, "3")); break;
                	case "void":tokens.add(new Token1(TokenType.KW, value, "4")); break;
                	case "return":tokens.add(new Token1(TokenType.KW, value, "5")); break;
                	case "const":tokens.add(new Token1(TokenType.KW, value, "6")); break;
                	case "main":tokens.add(new Token1(TokenType.KW, value, "7")); break;
                	case "struct":tokens.add(new Token1(TokenType.KW, value, "29")); break;
                	case "union":tokens.add(new Token1(TokenType.KW, value, "30")); break;
                	case "switch":tokens.add(new Token1(TokenType.KW, value, "31")); break;
                	case "case":tokens.add(new Token1(TokenType.KW, value, "32")); break;
                }
                pos += value.length();
                state = 0;
                break;
                case 2:
                value = matchAndConsume(input, pos, SE_PATTERN);
                switch (value) {
                	case "(":tokens.add(new Token1(TokenType.SE, value, "23")); break;
                	case ")":tokens.add(new Token1(TokenType.SE, value, "24")); break;
                	case "{":tokens.add(new Token1(TokenType.SE, value, "25")); break;
                	case "}":tokens.add(new Token1(TokenType.SE, value, "26")); break;
                	case ";":tokens.add(new Token1(TokenType.SE, value, "27")); break;
                	case ",":tokens.add(new Token1(TokenType.SE, value, "28")); break;
                }
                pos += value.length();
                state = 0;
                break;
                case 3:
                value = matchAndConsume(input, pos, OP_PATTERN);
                switch (value) {
                	case "!":tokens.add(new Token1(TokenType.OP, value, "8")); break;
                	case "+":tokens.add(new Token1(TokenType.OP, value, "9")); break;
                	case "-":tokens.add(new Token1(TokenType.OP, value, "10")); break;
                	case "*":tokens.add(new Token1(TokenType.OP, value, "11")); break;
                	case "/":tokens.add(new Token1(TokenType.OP, value, "12")); break;
                	case "%":tokens.add(new Token1(TokenType.OP, value, "13")); break;
                	case "=":tokens.add(new Token1(TokenType.OP, value, "14")); break;
                	case ">":tokens.add(new Token1(TokenType.OP, value, "15")); break;
                	case "<":tokens.add(new Token1(TokenType.OP, value, "16")); break;
                	case "==":tokens.add(new Token1(TokenType.OP, value, "17")); break;
                	case "<=":tokens.add(new Token1(TokenType.OP, value, "18")); break;
                	case ">=":tokens.add(new Token1(TokenType.OP, value, "19")); break;
                	case "!=":tokens.add(new Token1(TokenType.OP, value, "20")); break;
                	case "&&":tokens.add(new Token1(TokenType.OP, value, "21")); break;
                	case "||":tokens.add(new Token1(TokenType.OP, value, "22")); break;
                }
                pos += value.length();
                state = 0;
                break;
                case 4:
                while(!Character.isWhitespace(input.charAt(pos))&&isDigital(input.charAt(pos))) {
                	value += input.charAt(pos);
                	pos++;
                }
                pos--;
                if(input.charAt(pos+1)=='.'&&isDigital(input.charAt(pos))){
                	valueF=value;
                	changeto(5);
                }
                else {
                	tokens.add(new Token1(TokenType.INT, value, value));
                	state = 0;
                	pos += value.length();
                	if(value.length()==1) pos++;
                	pos--;
                }
                break;
                case 5:
                if(input.charAt(pos+1)=='.'){
                	valueF += ".";
                	pos+=2;
                   	changeto(6);
                }
                break;
                case 6:
               	while(!Character.isWhitespace(input.charAt(pos))&&isDigital(input.charAt(pos))) {
                   	valueF += input.charAt(pos);
                  	pos++;
                }
                pos--;
                tokens.add(new Token1(TokenType.FLOAT, valueF, valueF));
                valueF = "";
                pos += value.length();
                pos++;
                state = 0;
                break;
                case 7:
                value = matchAndConsume(input, pos, IDN_PATTERN);
                tokens.add(new Token1(TokenType.IDN, value, value));
                pos += value.length();
                state = 0;
                break;
                case 8:
                changeto(9);
                break;
                case 9:
                value = matchAndConsume(input, pos, CHAR_PATTERN);
                tokens.add(new Token1(TokenType.CHAR, value, value.substring(1, value.length()-1)));
                pos += value.length();
                state = 0;
                break;
                case 10:
                changeto(11);
                break;
                case 11:
                value = matchAndConsume(input, pos, IDN_PATTERN);
                tokens.add(new Token1(TokenType.IDN, value, value));
                pos += value.length();
                state = 0;
                break;
            }
        }
        return tokens;
    }

    private boolean matchesPattern(String input, int pos, String pattern) {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(input.substring(pos));
        return m.find() && m.start() == 0;
    }

    private String matchAndConsume(String input, int pos, String pattern) {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(input.substring(pos));
        if (m.find() && m.start() == 0) {
            return m.group(0);
        } else {
            throw new IllegalArgumentException("Pattern not matched at position " + pos);
        }
    }

    private void changeto(int state){
        this.state = state;
    }

    private boolean isLetter(char ch) {
		if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
			return true;
		else
			return false;
	}
    private boolean isDigital(char ch) {
		if (ch >= 48 && ch <= 57)
			return true;
		else
			return false;
	}
   // public LexicalAnalyzer(List<Token> tokens) {
      //  this.tokens = tokens;
   // }
}
